// Copyright 2006 Mloofle Inc. All Rights Reserved.
// Developed via Cleanroom

import java.util.BitSet;

/**
 * Represents a single word query; the word must match for the Query to match a
 * particular line.
 */
public class WordNode implements Query {
  /*
   * INVARIANTS: the contained word is canonicalized to lower case; the hases
   * are precomputed and final.
   */

  public WordNode(String word) {
    // canonicalize word to lowercase
    this.word = Util.toLowerCase(word);
    // precompute hashes
    this.hashes = Hasher.computeHashes(word, Util.STRONG_HASHES);
  }

  public boolean matchesFilter(BitSet bloomFilter) {
    // PRE: bloomFilter is non-null
    // PRE: hashes is initialized
    int nBits = bloomFilter.size();
    int nHash = Util.STRONG_HASHES;
    int[] filterPos = new int[nHash];
    for (int i = 0; i < nHash; ++i) {
      filterPos[i] = hashes[i] & (nBits - 1);
    }
    for (int i = 0; i < nHash; ++i) {
      if (!bloomFilter.get(filterPos[i])) {
        return false;
      }
    }
    return true;
    // POST: true is returned IFF all hashed bit positions in the filter are
    // true
  }

  public boolean matchesLineText(String lineText, CharacterAnalyzer analyzer) {
    // PRE: word is non-null and lowercase
    lineText = Util.toLowerCase(lineText);
    for (int pos = lineText.indexOf(word); pos >= 0; pos = lineText.indexOf(
        word, pos + 1)) {
      // ASSERT: we found a position in the text that matches this word

      // Make sure the match is an entire word and not a part of a word
      if (pos > 0 && !analyzer.isBreakChar(lineText.charAt(pos - 1))) {
        // not the beginning of a word; try next position
        continue;
      }
      // ASSERT: either the character before the match is a break character or
      // the match is at the beginning of the line
      int end = pos + word.length();
      if (end < lineText.length()
          && !analyzer.isBreakChar(lineText.charAt(end))) {
        // not the end of a word; try next position
        continue;
      }
      // ASSERT: either the character after the match is a break character or
      // the match is at the end of the line

      // ASSERT: the match is a whole word; this is a true match
      return true;
    }
    return false;
    // POST: true is returned IFF this word is a whole word in lineText
  }

  public String toString() {
    return word;
  }

  private final int[] hashes;
  private final String word;

}
